using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Timers;

namespace SpoonCore.Message.Info.Core
{
	public class ConfirmationManager
	{
		private const int Interval = 1000;
		private SortedList<int, ConfirmationItem> _itemList = new SortedList<int, ConfirmationItem>();
		private SortedList<int, IFaildHandlerItem> _faildHandlerList = new SortedList<int, IFaildHandlerItem>();
		private Timer _timer = new Timer(Interval);
		private SpoonClient _client;

		public delegate void OnConfirmationFaildHandler<T>(T message) where T : IMessage, new();

		public ConfirmationManager(SpoonClient client)
		{
			_client = client;
			_timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
			_timer.Start();
		}

		private void TimerElapsed(object sender, ElapsedEventArgs e)
		{
			List<ConfirmationItem> tmpList = new List<ConfirmationItem>();
			List<ConfirmationItem> oldList = new List<ConfirmationItem>();

			lock (_itemList)
			{
				foreach (ConfirmationItem item in _itemList.Values)
				{
					tmpList.Add(item);
				}
				_itemList.Clear();
			}

			foreach (ConfirmationItem item in tmpList)
			{
				if (item.Start.AddSeconds(item.Info.Timeout) < DateTime.Now)
				{
					// Timeout / resend
					if (item.Info.Ttl > 0)
					{
						item.Info.Ttl--;
						_client.Send(item.Destination, item.Message);
					}
					else
					{
						// item.Info.Faild(item.Message, item.Info, item.Destination);
						// Remove destination endpoint
						_client.RemoveClient(item.Destination);

						if(_faildHandlerList.ContainsKey(item.Message.MsgId))_faildHandlerList[item.Message.MsgId].Receive(item.Message);
					}
				}
				else
				{
					oldList.Add(item);
				}
			}

			lock (_itemList)
			{
				foreach (ConfirmationItem item in oldList)
				{
					_itemList.Add(item.Info.Id, item);
				}
			}
		}

		public void Register(IMessage msg, IPEndPoint dest, ConfirmationInfo info)
		{
			if (info.Status == ConfirmationInfo.ValidationStatus.Request)
			{
				lock (_itemList)
				{
					if (!_itemList.ContainsKey(info.Id)) _itemList.Add(info.Id, new ConfirmationItem(msg, dest, info));
				}
			}
		}

		public void Receive(ConfirmationInfo info, IPEndPoint source)
		{
			if (info.Status == ConfirmationInfo.ValidationStatus.Request)
			{
				ConfirmationInfo i = new ConfirmationInfo(info.Id, ConfirmationInfo.ValidationStatus.Response);
				_client.Send(source, new InfoMessage(i));
			}
			if (info.Status == ConfirmationInfo.ValidationStatus.Response)
			{
				lock (_itemList)
				{
					if (_itemList.ContainsKey(info.Id)) _itemList.Remove(info.Id);
				}
			}
		}

		public void RegisterFaildHandler<T>(OnConfirmationFaildHandler<T> handler) where T : IMessage, new()
		{
			T tmp = new T();
			if(!_faildHandlerList.ContainsKey(tmp.MsgId))_faildHandlerList.Add(tmp.MsgId, new FaildHandlerItem<T>(handler));
		}

		private interface IFaildHandlerItem
		{
			void Receive(IMessage msg);
		}
		private class FaildHandlerItem<T> : IFaildHandlerItem where T : IMessage, new()
		{
			private OnConfirmationFaildHandler<T> _handler;
			public FaildHandlerItem(OnConfirmationFaildHandler<T> handler)
			{
				_handler = handler;
			}
			public void Receive(IMessage msg)
			{
				if (_handler != null) _handler.Invoke((T)msg);
			}
		}

		private class ConfirmationItem
		{
			public IMessage Message;
			public IPEndPoint Destination;
			public ConfirmationInfo Info;
			public DateTime Start = DateTime.Now;

			public ConfirmationItem(IMessage msg, IPEndPoint dest, ConfirmationInfo info)
			{
				Message = msg;
				Destination = dest;
				Info = info;
			}
		}
	}
}